// File provided for Microsoft Shared Source.
// Copyright (c) Microsoft Corporation. All rights reserved.

#include "stdafx.h"
#include "CWMIFldr.h"
#include "StrRet.h"
#include "WMIEnumerator.h"
#include "shlobj.h"

typedef HRESULT (CWMIFldr::*DisplayNameFn)(PCUITEMID_CHILD pidl, PWSTR *ppsz);

const COLUMNINFO CWMIFldr::s_rgColumns[] = 
{
    {LVCFMT_LEFT, 20, IDS_COLNAME_IDENTITY,   SHCOLSTATE_TYPE_STR | SHCOLSTATE_HIDDEN,                          PKEY_ParsingPath },
    {LVCFMT_LEFT, 20, IDS_COLNAME_NAME,       SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,                     PKEY_ItemNameDisplay },
    {LVCFMT_LEFT, 25, IDS_COLNAME_TYPE,       SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,                     PKEY_ItemTypeText },
    {LVCFMT_LEFT, 25, IDS_COLNAME_CHILDCOUNT, SHCOLSTATE_SLOW | SHCOLSTATE_TYPE_INT,                            PKEY_FileCount },
};

CWMIFldr::CWMIFldr()
{
    InterlockedIncrement(&g_cDllLock);

    _cRef = 1;
    _pidl = NULL;
}

CWMIFldr::~CWMIFldr()
{
    ILFree(_pidl);

    InterlockedDecrement(&g_cDllLock);
}

HRESULT CWMIFldr::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv)
{
    TRACEFNENTER_IID(riid);

    HRESULT hr;
    if (pUnkOuter)
    {
        hr = CLASS_E_NOAGGREGATION;
    }
    else
    {
        CWMIFldr *pWMIFldr = new CWMIFldr();
        if (pWMIFldr)
        {
            hr = pWMIFldr->QueryInterface(riid, ppv);
            if (FAILED(hr))
            {
                delete pWMIFldr;
            }
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }

    TRACEFNEXIT();
    return hr;
}

HRESULT CWMIFldr::LockServer(BOOL fLock)
{
    HRESULT hr = S_OK;
    if (fLock)
    {
        InterlockedIncrement(&g_cDllLock);
    }
    else
    {
        InterlockedDecrement(&g_cDllLock);
    }
    return hr;
}


HRESULT CWMIFldr::QueryInterface(REFIID riid, void **ppv)
{
    //TRACEFNENTER_IID(riid);

    HRESULT hr = E_NOINTERFACE;
    *ppv = NULL;

    QI_ENTRY_EX(IUnknown, IShellFolder);

    QI_ENTRY(IShellFolder);
    QI_ENTRY(IShellFolder2);
    QI_ENTRY_EX(IPersist, IPersistFolder2);
    QI_ENTRY(IPersistFolder);
    QI_ENTRY(IPersistFolder2);
    QI_ENTRY(IClassFactory);
    QI_ENTRY(IPersistIDList);

    TRACEFNEXIT_IID(riid);
    return hr;
}

// CWMIFldr

HRESULT CWMIFldr::ParseDisplayName( 
                                   /* [in] */ HWND hwnd,
                                   /* [in] */ LPBC pbc,
                                   /* [string][in] */ LPOLESTR pszDisplayName,
                                   /* [out] */ ULONG *pchEaten,
                                   /* [out] */ PIDLIST_RELATIVE *ppidl,
                                   /* [unique][out][in] */ ULONG *pdwAttributes)
{
    TRACEFNENTER();
    HRESULT hr;
    *ppidl = NULL;

    CComPtr<IWbemClassObject> spObj;
    hr = s_GetObjectForParsingPath(pszDisplayName, &spObj);
    if (SUCCEEDED(hr))
    {
        hr = CWMIFldr::s_CreateIDList(spObj, (PITEMID_CHILD*)ppidl);
    }

    TRACEFNEXIT();
    return hr;
}

HRESULT CWMIFldr::EnumObjects( 
                              /* [in] */ HWND hwnd,
                              /* [in] */ SHCONTF grfFlags,
                              /* [out] */ IEnumIDList **ppenumIDList)
{
    TRACEFNENTER();
    s_TraceIDList(_pidl);

    WMIENUMINFO wmiEnumInfo;
    wmiEnumInfo.pszNamespace = L"root";

    CComVariant svarParsingPath;
    HRESULT hr = GetPropertyFromIDList(ILFindLastID(_pidl), PKEY_ParsingPath, &svarParsingPath);
    if (SUCCEEDED(hr))
    {
        wmiEnumInfo.InitFromPath(svarParsingPath.bstrVal);
    }
    else
    {
        // OK, treat it as the root
        hr = S_OK;
    }

    if (SUCCEEDED(hr))
    {
        if (!wmiEnumInfo.pszInstance)
        {
            hr = CWMIEnumerator::Create(&wmiEnumInfo, IID_PPV_ARGS(ppenumIDList));
        } else {
            hr = CWMIInstanceEnumerator::Create(&wmiEnumInfo, IID_PPV_ARGS(ppenumIDList));
        }
    }

    TRACEFNEXIT();
    return hr;
}

HRESULT CWMIFldr::BindToObject( 
                               /* [in] */ PCUIDLIST_RELATIVE pidl,
                               /* [in] */ LPBC pbc,
                               /* [in] */ REFIID riid,
                               /* [iid_is][out] */ void **ppv)
{
    HRESULT hr = E_NOINTERFACE;
    TRACEFNENTER_IID(riid);
    *ppv = NULL;

    if (IsEqualIID(riid, __uuidof(IShellFolder)) || IsEqualIID(riid, __uuidof(IShellFolder2)))
    {
        hr = _CreateFolder(reinterpret_cast<PCUITEMID_CHILD>(pidl), riid, ppv);
    }

    TRACEFNEXIT_IID(riid);
    return hr;
}

HRESULT CWMIFldr::BindToStorage( 
                                /* [in] */ PCUIDLIST_RELATIVE pidl,
                                /* [in] */ LPBC pbc,
                                /* [in] */ REFIID riid,
                                /* [iid_is][out] */ void **ppv)
{
    *ppv = NULL;
    TRACEFNENTER_IID(riid);
    HRESULT hr = E_NOTIMPL;
    TRACEFNEXIT();
    return hr;
}

HRESULT CWMIFldr::_CompareProperties(PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2, const PROPERTYKEY &pkey)
{
    CComVariant svar1;
    HRESULT hr = GetDetailsEx((PCUITEMID_CHILD)pidl1, &pkey, &svar1);
    if (SUCCEEDED(hr))
    {
        CComVariant svar2;
        hr = GetDetailsEx((PCUITEMID_CHILD)pidl2, &pkey, &svar2);
        if (SUCCEEDED(hr))
        {
            hr = MAKE_HRESULT(SEVERITY_SUCCESS, 0, (USHORT)VariantCompare(svar1, svar2));
        }
    }
    return hr;
}

HRESULT CWMIFldr::CompareIDs( 
                             /* [in] */ LPARAM lParam,
                             /* [in] */ PCUIDLIST_RELATIVE pidl1,
                             /* [in] */ PCUIDLIST_RELATIVE pidl2)
{
    TRACEFNENTER();
    HRESULT hr;

    WORD iColPrimary = LOWORD(lParam);
    WORD ffComparison = HIWORD(lParam);

    if (iColPrimary < ARRAYSIZE(s_rgColumns))
    {
        hr = _CompareProperties(pidl1, pidl2, s_rgColumns[iColPrimary].pkey);
        if (SHCIDS_ALLFIELDS & ffComparison && S_OK == hr)
        {
            // the first field didn't do anything to sort it - compare the others.
            for (int iCol = 0; S_OK == hr && iCol < ARRAYSIZE(s_rgColumns); iCol++)
            {
                if (iCol == iColPrimary)
                {
                    continue;
                }
                hr = _CompareProperties(pidl1, pidl2, s_rgColumns[iCol].pkey);
            }
        }

        if (S_OK == hr)
        {
            // There may be multiple levels in the IDLIST(s).  Compare those also
            hr = _CompareRelativeIDLists(lParam, pidl1, pidl2);
        }
    }
    else
    {
        hr = E_INVALIDARG;
    }

    TRACEFNEXIT();
    return hr;
}

HRESULT CWMIFldr::_CompareRelativeIDLists(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
{
    HRESULT hr = S_OK;
    short sResult = 0;
    PCUIDLIST_RELATIVE pidlRel1 = ILNext(pidl1);
    PCUIDLIST_RELATIVE pidlRel2 = ILNext(pidl2);
    if (ILIsEmpty(pidlRel1))
    {
        if (ILIsEmpty(pidlRel2))
        {
            sResult = 0;
        }
        else
        {
            sResult = -1;
        }
    }
    else
    {
        if (ILIsEmpty(pidlRel2))
        {
            sResult = 1;
        }
        else
        {
            // Relative IDLISTs both have data, bind to them and let the subfolder do the comparison
            PITEMID_CHILD pidlNext;
            hr = CloneFirstIDList(pidl1, &pidlNext);
            if (SUCCEEDED(hr))
            {
                CComPtr<IShellFolder2> spsf2;
                hr = BindToObject(pidlNext, NULL, IID_PPV_ARGS(&spsf2));
                if (SUCCEEDED(hr))
                {
                    hr = spsf2->CompareIDs(lParam & ~SHCIDS_COLUMNMASK, pidlRel1, pidlRel2);
                    if (SUCCEEDED(hr))
                    {
                        sResult = ShortFromResult(hr);
                    }
                }
                ILFree(pidlNext);
            }
        }
    }
    if (SUCCEEDED(hr))
    {
        hr = CompareResultFromInt(sResult);
    }
    return hr;
}

HRESULT CWMIFldr::CreateViewObject( 
                                   /* [in] */ HWND hwndOwner,
                                   /* [in] */ REFIID riid,
                                   /* [iid_is][out] */ void **ppv)
{
    HRESULT hr;
    TRACEFNENTER_IID(riid);
    *ppv = NULL;

    if (IsEqualIID(riid, __uuidof(IShellView)))
    {
        SFV_CREATE sfvc = {sizeof(sfvc)};
        sfvc.pshf = this;

        hr = SHCreateShellFolderView(&sfvc, (IShellView**)ppv);
    }
    else
    {
        hr = E_NOTIMPL;
    }

    TRACEFNEXIT_IID(riid);
    return hr;
}

HRESULT CWMIFldr::GetAttributesOf( 
                                  /* [in] */ UINT cidl,
                                  /* [size_is][in] */ PCUITEMID_CHILD_ARRAY apidl,
                                  /* [out][in] */ SFGAOF *rgfInOut)
{
    TRACEFNENTER();
    DWORD sfgaof = *rgfInOut;
    HRESULT hr = S_OK;
    for (UINT iPidl = 0; iPidl < cidl && SUCCEEDED(hr); iPidl++)
    {
        const WMIINFO *pInfo;
        hr = GetDataFromIDList(apidl[iPidl], &pInfo);
        sfgaof = sfgaof & pInfo->sfgaof;
    }
    *rgfInOut = sfgaof;

    TRACEFNEXIT();
    return hr;
}

HRESULT CWMIFldr::GetUIObjectOf( 
                                /* [in] */ HWND hwndOwner,
                                /* [in] */ UINT cidl,
                                /* [size_is][in] */ PCUITEMID_CHILD_ARRAY apidl,
                                /* [in] */ REFIID riid,
                                /* [unique][out][in] */ UINT *rgfReserved,
                                /* [iid_is][out] */ void **ppv)
{
    TRACEFNENTER_IID(riid);
    *ppv = NULL;

    HRESULT hr = E_NOINTERFACE;
    if (riid == __uuidof(IDataObject))
    {
        hr = SHCreateDataObject(_pidl, cidl, apidl, NULL, riid, ppv);
    }
    else if (cidl == 1)
    {
        if (IsEqualIID(riid, __uuidof(IExtractIconW)) || IsEqualIID(riid, __uuidof(IExtractIconA)))
        {
            const WMIINFO *pInfo;
            hr = GetDataFromIDList(apidl[0], &pInfo);
            if (SUCCEEDED(hr))
            {
                WCHAR szModulePath[MAX_PATH];
                if (GetModuleFileName(_AtlBaseModule.GetModuleInstance(), szModulePath, ARRAYSIZE(szModulePath)))
                {
                    int iIcon = 0;
                    switch (pInfo->eObjType)
                    {
                    case WMIOT_Namespace:
                        GetSystemDirectory(szModulePath, ARRAYSIZE(szModulePath));
                        PathAppend(szModulePath, L"comres.dll");
                        break;

                    case WMIOT_Class:
                        iIcon = -IDI_CLASS;
                        break;

                    case WMIOT_Instance:
                        iIcon = -IDI_INSTANCE;
                        break;

                    case WMIOT_Property:
                        iIcon = -IDI_PROPERTY;
                        break;
                    }

                    CComPtr<IDefaultExtractIconInit> spdxi;
                    hr = SHCreateDefaultExtractIcon(IID_PPV_ARGS(&spdxi));
                    if (SUCCEEDED(hr))
                    {
                        // BUGBUG: GIL_PERINSTANCE == inefficient
                        hr = spdxi->SetFlags(GIL_PERINSTANCE);
                        if (SUCCEEDED(hr))
                        {
                            hr = spdxi->SetNormalIcon(szModulePath, iIcon);
                            if (SUCCEEDED(hr))
                            {
                                hr = spdxi->QueryInterface(riid, ppv);
                            }
                        }
                    }
                }
                else
                {
                    hr = E_FAIL;
                }
            }
        }
        else if (IsEqualIID(riid, __uuidof(IContextMenu)))
        {
            DEFCONTEXTMENU dcm = { hwndOwner, NULL, _pidl, reinterpret_cast<IShellFolder2 *>(this), cidl, apidl, NULL, 0, NULL };
            hr = SHCreateDefaultContextMenu(&dcm, riid, ppv);
        }
        else if (IsEqualIID(riid, __uuidof(IQueryAssociations)))
        {
            CComPtr<IQueryAssociations> spQueryAssociations;
            hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARGS(&spQueryAssociations));
            if (SUCCEEDED(hr))
            {
                hr = spQueryAssociations->Init(ASSOCF_INIT_DEFAULTTOFOLDER, L"WMI.Object", NULL, NULL);
                if (SUCCEEDED(hr))
                {
                    hr = spQueryAssociations->QueryInterface(riid, ppv);
                }
            }
        }
        else
        {
            hr = E_NOINTERFACE;
        }

    }

    TRACEFNEXIT_IID(riid);
    return hr;
}

HRESULT CWMIFldr::_GetRelativeDisplayName(PCUITEMID_CHILD pidl, PWSTR *ppsz)
{
    PROPVARIANT propvar;
    HRESULT hr = GetPropertyFromIDList(pidl, PKEY_ItemNameDisplay, &propvar);
    if (SUCCEEDED(hr))
    {
        hr = PropVariantToStringAlloc(propvar, ppsz);
        PropVariantClear(&propvar);
    }
    return hr;
}

HRESULT CWMIFldr::_GetAbsoluteDisplayName(PCUITEMID_CHILD pidl, PWSTR *ppsz)
{
    // not sure how to make this more human-readable - have to worry about this later
    return _GetRelativeDisplayName(pidl, ppsz);
}

HRESULT CWMIFldr::_GetAbsoluteParsingName(PCUITEMID_CHILD pidl, PWSTR *ppsz)
{
    PROPVARIANT propvar;
    HRESULT hr = GetPropertyFromIDList(pidl, PKEY_ParsingPath, &propvar);
    if (SUCCEEDED(hr))
    {
        hr = PropVariantToStringAlloc(propvar, ppsz);
        PropVariantClear(&propvar);
    }
    return hr;
}

HRESULT CWMIFldr::_GetRelativeParsingName(PCUITEMID_CHILD pidl, PWSTR *ppsz)
{
    return _GetAbsoluteParsingName(pidl, ppsz);
}

HRESULT CWMIFldr::GetDisplayNameOf( 
                                   /* [in] */ PCUITEMID_CHILD pidl,
                                   /* [in] */ SHGDNF uFlags,
                                   /* [out] */ STRRET *pName)
{
    TRACEFNENTER();
    HRESULT hr;

    ZeroMemory(pName, sizeof(*pName));

    if (!ILIsEmpty(pidl))
    {
        static const DisplayNameFn s_rgSIGDNMapping[2][2] = 
        {
            // unfriendly                          // friendly
            {{&CWMIFldr::_GetAbsoluteParsingName}, {&CWMIFldr::_GetAbsoluteDisplayName}}, // absolute
            {{&CWMIFldr::_GetRelativeParsingName}, {&CWMIFldr::_GetRelativeDisplayName}}, // relative
        };

        bool fRelative = (uFlags & SHGDN_INFOLDER) == SHGDN_INFOLDER;
        bool fFriendly = (uFlags & SHGDN_FORPARSING) != SHGDN_FORPARSING 
            || (uFlags & SHGDN_FORADDRESSBAR) == SHGDN_FORADDRESSBAR;

        PWSTR pszDisplay;
        hr = (this->*s_rgSIGDNMapping[fRelative][fFriendly])(pidl, &pszDisplay);
        if (SUCCEEDED(hr))
        {
            pName->uType = STRRET_WSTR;
            pName->pOleStr = pszDisplay;
        }
    }
    else
    {
        // S_FALSE means please get it from the registry instead
        hr = S_FALSE;
    }

    if (FAILED(hr))
    {
        hr = hr;
    }

    TRACEFNEXIT();
    return hr;
}

HRESULT CWMIFldr::SetNameOf( 
                            /* [in] */ HWND hwnd,
                            /* [in] */ PCUITEMID_CHILD pidl,
                            /* [string][in] */ LPCOLESTR pszName,
                            /* [in] */ SHGDNF uFlags,
                            /* [out] */ PITEMID_CHILD *ppidlOut)
{
    TRACEFNENTER();
    *ppidlOut = NULL;
    HRESULT hr = E_NOTIMPL;
    TRACEFNEXIT();
    return hr;
}

//////////////////
// IPersistFolder2
/////////

HRESULT CWMIFldr::Initialize(PCUIDLIST_ABSOLUTE pidl)
{
    TRACEFNENTER();

    HRESULT hr;

    if (_pidl)
    {
        ILFree(_pidl);
    }
    _pidl = ILCloneFull(pidl);

    hr = _pidl ? S_OK : E_OUTOFMEMORY;

    TRACEFNEXIT();
    return hr;
}

HRESULT CWMIFldr::GetClassID(CLSID *pClassID)
{
    *pClassID = __uuidof(CWMIFldr);
    return S_OK;
}

HRESULT CWMIFldr::GetCurFolder(PIDLIST_ABSOLUTE *ppidl)
{
    TRACEFNENTER();

    HRESULT hr;
    if (_pidl)
    {
        *ppidl = ILCloneFull(_pidl);
        hr = *ppidl ? S_OK : E_OUTOFMEMORY;
    }
    else
    {
        hr = E_FAIL;
    }

    TRACEFNEXIT();
    return hr;
}

//////////////////
// IShellFolder2
/////////

HRESULT CWMIFldr::EnumSearches(IEnumExtraSearch **ppEnum)
{
    //TRACEFNENTER();
    HRESULT hr;

    hr = E_NOTIMPL;

    TRACEFNEXIT();
    return hr;
}

HRESULT CWMIFldr::GetDefaultColumn(DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
{
    *pSort = 0;
    *pDisplay = 0;
    return S_OK;
}

HRESULT CWMIFldr::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags)
{
    //TRACEFNENTER();
    HRESULT hr;

    if (iColumn < ARRAYSIZE(s_rgColumns))
    {
        hr = S_OK;
        *pcsFlags = s_rgColumns[iColumn].shcolstatef;
    }
    else
    {
        hr = E_INVALIDARG;
    }

    TRACEFNEXIT();
    return hr;
}

HRESULT CWMIFldr::GetDefaultSearchGUID(GUID *pguid)
{
    //TRACEFNENTER();
    HRESULT hr;

    hr = E_NOTIMPL;

    TRACEFNEXIT();
    return hr;
}

HRESULT CWMIFldr::GetDetailsEx(PCUITEMID_CHILD pidl, const PROPERTYKEY *pkey, VARIANT *pv)
{
    TRACEFNENTER();
    HRESULT hr = GetPropertyFromIDList(pidl, *pkey, pv);
    if (FAILED(hr))
    {
        if (IsEqualPropertyKey(*pkey, PKEY_PropList_InfoTip) 
            || IsEqualPropertyKey(*pkey, PKEY_PropList_TileInfo) 
            || IsEqualPropertyKey(*pkey, PKEY_PropList_ExtendedTileInfo))
        {
            hr = InitVariantFromString(
                L"prop:"
                L"System.ItemTypeText;"
                , pv);
        }
        else if (IsEqualPropertyKey(*pkey, PKEY_PropList_PreviewTitle))
        {
            hr = InitVariantFromString(L"prop:System.ItemNameDisplay;System.ParsingPath;", pv);
        }
        else if (IsEqualPropertyKey(*pkey, PKEY_PropList_PreviewDetails))
        {
            hr = InitVariantFromString(
                L"prop:"
                L"System.ParsingPath;"
                L"System.ItemTypeText;"
                , pv);
        }
        else if (IsEqualPropertyKey(*pkey, PKEY_DescriptionID))
        {
            SHDESCRIPTIONID did = {SHDID_COMPUTER_OTHER};
            hr = InitVariantFromBuffer(&did, sizeof(did), pv);
        }
        else if (IsEqualPropertyKey(*pkey, PKEY_FileCount))
        {
            hr = _GetChildCount(pidl, pv);
        }
    }

    TRACEFNEXIT_PKEY(*pkey);
    return hr;
}

HRESULT CWMIFldr::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
{
    TRACEFNENTER();
    HRESULT hr;

    ZeroMemory(psd, sizeof(*psd));

    if (NULL == pidl)
    {
        if (iColumn < ARRAYSIZE(s_rgColumns))
        {
            // If pidl parameter is set to NULL, the title of the information field specified by iColumn is returned.
            psd->fmt = s_rgColumns[iColumn].lvcfmt;
            psd->cxChar = s_rgColumns[iColumn].cxChar;
            if (s_rgColumns[iColumn].dwResId)
            {
                hr = SetSTRRET(&psd->str, s_rgColumns[iColumn].dwResId);
            }
            else
            {
                hr = SetSTRRET(&psd->str, L"");
            }
        }
        else
        {
            hr = E_INVALIDARG;
        }
    }
    else
    {
        hr = E_NOTIMPL;
    }

    if (hr != E_INVALIDARG) 
    {
        TRACEFNEXIT();
    }
    return hr;
}

HRESULT CWMIFldr::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
{
    //TRACEFNENTER();
    HRESULT hr;

    if (iColumn < ARRAYSIZE(s_rgColumns))
    {
        memcpy(pscid, &s_rgColumns[iColumn].pkey, sizeof(*pscid));
        hr = S_OK;
    }
    else
    {
        hr = E_INVALIDARG;
    }

    TRACEFNEXIT();
    return hr;
}

/////////////////////////////
// IPersistIDList
/////////////


HRESULT CWMIFldr::GetIDList(PIDLIST_ABSOLUTE *ppidl)
{
    TRACEFNENTER();
    HRESULT hr;

    if (_pidl)
    {
        *ppidl = ILCloneFull(_pidl);
        hr = *ppidl ? S_OK : E_OUTOFMEMORY;
    }
    else
    {
        hr = E_FAIL;
    }

    TRACEFNEXIT();
    return hr;
}

HRESULT CWMIFldr::SetIDList(PCUIDLIST_ABSOLUTE pidl)
{
    TRACEFNENTER();
    HRESULT hr;

    PIDLIST_ABSOLUTE pidlT = ILCloneFull(pidl);
    if (pidlT)
    {
        ILFree(_pidl);
        _pidl = pidlT;
        hr = S_OK;
    }
    else
    {
        hr = E_FAIL;
    }

    TRACEFNEXIT();
    return hr;
}

HRESULT CWMIFldr::s_GetServer(__in_opt PCWSTR pszComputer, __in_opt PCWSTR pszNamespace, __deref_out IWbemServices **ppWbemServices)
{
    *ppWbemServices = NULL;

    HRESULT hr = CoInitializeSecurity(
        NULL,                       // security descriptor
        -1,                          // use this simple setting
        NULL,                        // use this simple setting
        NULL,                        // reserved
        RPC_C_AUTHN_LEVEL_DEFAULT,   // authentication level  
        RPC_C_IMP_LEVEL_IMPERSONATE, // impersonation level
        NULL,                        // use this simple setting
        EOAC_NONE,                   // no special capabilities
        NULL);                       // reserved

    if (RPC_E_TOO_LATE == hr)
    {
        // if the security is already initialized, that's great (we hope).  
        // If it isn't what we need, we'll fail later (I assume)
        hr = S_OK;
    }

    if (SUCCEEDED(hr))
    {
        CComPtr<IWbemLocator> spLoc = 0;
        hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&spLoc));
        if (SUCCEEDED(hr))
        {
            CComPtr<IWbemServices> spSvc = 0;

            CComBSTR sbstrConnection;
            hr = sbstrConnection.Append(pszNamespace);
            if (SUCCEEDED(hr))
            {
                // Connect to the root\default namespace with the current user.
                hr = spLoc->ConnectServer(sbstrConnection, NULL, NULL, 0, NULL, 0, 0, &spSvc);

                if (SUCCEEDED(hr))
                {
                    // Set the proxy so that impersonation of the client occurs.
                    hr = CoSetProxyBlanket(spSvc,
                        RPC_C_AUTHN_WINNT,
                        RPC_C_AUTHZ_NONE,
                        NULL,
                        RPC_C_AUTHN_LEVEL_CALL,
                        RPC_C_IMP_LEVEL_IMPERSONATE,
                        NULL,
                        EOAC_NONE);
                    if (SUCCEEDED(hr))
                    {
                        *ppWbemServices = spSvc.Detach();
                    }
                }
            }
        }
    }
    return hr;
}

HRESULT CWMIFldr::s_GetObjectForParsingPath(PCWSTR pszParsingPath, __out IWbemClassObject **ppObj)
{
    HRESULT hr;

    WMIENUMINFO wei;
    hr = wei.InitFromPath(pszParsingPath);
    if (SUCCEEDED(hr))
    {
        wei.GetInstance(ppObj);
    }

    return hr;
}

HRESULT CWMIFldr::s_GetObjectForIDList(PCUITEMID_CHILD pidl, const WMIINFO **ppWMIInfo, __out IWbemClassObject **ppObj)
{
    const WMIINFO *pInfo;
    HRESULT hr = GetDataFromIDList(pidl, &pInfo);
    if (SUCCEEDED(hr))
    {
        PROPVARIANT propvarPath;
        hr = GetPropertyFromIDList(pidl, PKEY_ParsingPath, &propvarPath);
        if (SUCCEEDED(hr))
        {
            hr = s_GetObjectForParsingPath(propvarPath.bstrVal, ppObj);
            if (SUCCEEDED(hr) && ppWMIInfo)
            {
                *ppWMIInfo = pInfo;
            }
            PropVariantClear(&propvarPath);
        }
    }

    return hr;
}

HRESULT CWMIFldr::s_TraceIDList(PCUIDLIST_RELATIVE pidl)
{
    return s_TraceIDList(ILFindLastID(pidl));
}

HRESULT CWMIFldr::s_TraceIDList(PCUITEMID_CHILD pidl)
{
    HRESULT hr;

    CComPtr<IPropertyStore> spsCachedProperties;
    hr = GetPropertyStorageFromIDList(pidl, IID_PPV_ARGS(&spsCachedProperties));
    if (SUCCEEDED(hr))
    {
        DWORD cProps;
        hr = spsCachedProperties->GetCount(&cProps);
        for (DWORD iProp = 0; SUCCEEDED(hr) && iProp < cProps; iProp++)
        {
            PROPERTYKEY pkey;
            hr = spsCachedProperties->GetAt(iProp, &pkey);
            if (SUCCEEDED(hr))
            {
                PROPVARIANT propVar;
                hr = spsCachedProperties->GetValue(pkey, &propVar);
                if (SUCCEEDED(hr))
                {
                    WCHAR psz[MAX_PATH * 2]; // we hold munged paths which can add some chars - this is overkill for that, but there's no length restriction on properties
                    hr = PropVariantToString(propVar, psz, ARRAYSIZE(psz));
                    if (SUCCEEDED(hr))
                    {
                        Trace(L"PIDLDUMP: %p-%s (%s)\r\n", pidl, SmartStringFromPKEY(pkey), psz);
                    }
                    PropVariantClear(&propVar);
                }
            }
        }
    }
    return hr;
}

HRESULT CWMIFldr::s_CreateIDList(__in IWbemClassObject *pObjWeak, __deref_out PITEMID_CHILD *ppidlChild)
{
    WMIINFO wmiInfo = {WMIOT_Invalid};
    wmiInfo.sfgaof = SFGAO_CANLINK | SFGAO_BROWSABLE | SFGAO_FOLDER;

    CComPtr<IPropertyStore> spsCachedProperties;
    HRESULT hr = PSCreateMemoryPropertyStore(IID_PPV_ARGS(&spsCachedProperties));
    if (SUCCEEDED(hr))
    {
        PROPVARIANT propvarPath;
        hr = s_GetStringProperty(pObjWeak, L"__Path", &propvarPath);
        if (SUCCEEDED(hr))
        {
            hr = spsCachedProperties->SetValue(PKEY_ParsingPath, propvarPath);
            if (SUCCEEDED(hr))
            {
                PCWSTR pszNameProperty = L"Name";

                PROPVARIANT propvarClass;
                hr = s_GetStringProperty(pObjWeak, L"__Class", &propvarClass);
                if (SUCCEEDED(hr))
                {
                    CComVariant varProp;
                    CIMTYPE cimType;
                    hr = pObjWeak->Get(L"__GENUS", 0, &varProp, &cimType, NULL);
                    if (SUCCEEDED(hr))
                    {
                        ATLASSERT(cimType == CIM_SINT32);
                        switch (varProp.intVal)
                        {
                        case WBEM_GENUS_CLASS:
                            wmiInfo.eObjType = WMIOT_Class;
                            pszNameProperty = L"__CLASS";
                            {
                                PROPVARIANT propvarSuperClass;
                                hr = s_GetStringProperty(pObjWeak, L"__SUPERCLASS", &propvarSuperClass);
                                if (SUCCEEDED(hr))
                                {
                                    hr = spsCachedProperties->SetValue(PKEY_ItemTypeText, propvarSuperClass);
                                    if (SUCCEEDED(hr))
                                    {
                                        hr = spsCachedProperties->SetValue(PKEY_ItemType, propvarSuperClass);
                                    }
                                    PropVariantClear(&propvarSuperClass);
                                }

                                if (SUCCEEDED(hr))
                                {
                                    // if it's an abstract class, hide it
                                    CComPtr<IWbemQualifierSet> spQualifierSet;
                                    HRESULT hrInner = pObjWeak->GetQualifierSet(&spQualifierSet);
                                    if (SUCCEEDED(hrInner))
                                    {
                                        CComVariant svarIsAbstract;
                                        hrInner = spQualifierSet->Get(L"Abstract", 0, &svarIsAbstract, NULL);
                                        if (SUCCEEDED(hrInner) && VT_BOOL == svarIsAbstract.vt && svarIsAbstract.boolVal)
                                        {
                                            wmiInfo.sfgaof |= SFGAO_HIDDEN;
                                        }
                                    }
                                }
                            }
                            break;

                        case WBEM_GENUS_INSTANCE:
                            if (propvarClass.bstrVal && wcscmp(propvarClass.bstrVal, L"__NAMESPACE") == 0)
                            {
                                wmiInfo.eObjType = WMIOT_Namespace;
                            }
                            else
                            {
                                wmiInfo.eObjType = WMIOT_Instance;
                            }
                            hr = spsCachedProperties->SetValue(PKEY_ItemTypeText, propvarClass);
                            if (SUCCEEDED(hr))
                            {
                                hr = spsCachedProperties->SetValue(PKEY_ItemType, propvarClass);
                            }
                            break;

                            DEFAULT_UNREACHABLE;
                        }
                    }
                    PropVariantClear(&propvarClass);
                }

                if (SUCCEEDED(hr))
                {
                    if (wmiInfo.eObjType == WMIOT_Class || wmiInfo.eObjType == WMIOT_Namespace)
                    {
                        wmiInfo.sfgaof |= SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE | SFGAO_FOLDER;
                    }
                    PROPVARIANT propvarName;
                    hr = s_GetStringProperty(pObjWeak, pszNameProperty, &propvarName);
                    if (SUCCEEDED(hr))
                    {
                        if (propvarName.vt == VT_NULL)
                        {
                            CComPtr<IWbemPath> spPathParser;
                            hr = CoCreateInstance(__uuidof(WbemDefPath), 0, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&spPathParser));
                            if (SUCCEEDED(hr))
                            {
                                hr = spPathParser->SetText(WBEMPATH_CREATE_ACCEPT_ALL, propvarPath.bstrVal);
                                if (SUCCEEDED(hr) || WBEM_E_INVALID_PARAMETER == hr)
                                {
                                    CComPtr<IWbemPathKeyList> spKeyList;
                                    hr = spPathParser->GetKeyList(&spKeyList);
                                    if (SUCCEEDED(hr))
                                    {
                                        WCHAR szInstance[MAX_PATH];
                                        ULONG cchInstance = ARRAYSIZE(szInstance);
                                        hr = spKeyList->GetText(WBEMPATH_QUOTEDTEXT, &cchInstance, szInstance);
                                        if (SUCCEEDED(hr) && wcslen(szInstance) > 0)
                                        {
                                            hr = InitPropVariantFromString(szInstance, &propvarName);
                                        }
                                    }
                                }
                            }
                        }

                        if (SUCCEEDED(hr))
                        {
                            if (0 == wcsncmp(propvarName.bstrVal, L"__", ARRAYSIZE(L"__")-1))
                            {
                                hr = E_INVALIDARG;
                            }
                            else if (propvarClass.bstrVal && 0 == wcsncmp(propvarClass.bstrVal, L"__SystemClass", ARRAYSIZE(L"__SystemClass")-1))
                            {
                                hr = E_INVALIDARG;
                            }
                        }

                        if (SUCCEEDED(hr))
                        {
                            hr = spsCachedProperties->SetValue(PKEY_ItemNameDisplay, propvarName);
                            if (SUCCEEDED(hr))
                            {
                                hr = CWMIFldr::s_CreateItemID(&wmiInfo, spsCachedProperties, ppidlChild);
                            }
                        }
                        PropVariantClear(&propvarName);
                    }
                }
            }
            PropVariantClear(&propvarPath);
        }
    }

    TRACEFNEXIT();
    return hr;
}

HRESULT CWMIFldr::s_CreateIDList(__in IWbemClassObject *pInstance, PCWSTR pszPropertyName, __deref_out PITEMID_CHILD *ppidlChild)
{
    WMIINFO wmiInfo = {WMIOT_Property};
    wmiInfo.sfgaof = 0;

    CComPtr<IPropertyStore> spsCachedProperties;
    HRESULT hr = PSCreateMemoryPropertyStore(IID_PPV_ARGS(&spsCachedProperties));
    if (SUCCEEDED(hr))
    {
        PROPVARIANT propvarName = {0};
        hr = InitPropVariantFromString(pszPropertyName, &propvarName);
        if (SUCCEEDED(hr))
        {
            hr = spsCachedProperties->SetValue(PKEY_ItemNameDisplay, propvarName);
            PropVariantClear(&propvarName);
        }

        if (SUCCEEDED(hr))
        {
            CComVariant svar;
            hr = pInstance->Get(pszPropertyName, 0, &svar, NULL, NULL);
            if (SUCCEEDED(hr))
            {
                hr = svar.ChangeType(VT_BSTR);
                if (SUCCEEDED(hr))
                {
                    PROPVARIANT propVar;
                    hr = VariantToPropVariant(&svar, &propVar);
                    if (SUCCEEDED(hr))
                    {
                        hr = spsCachedProperties->SetValue(PKEY_ItemTypeText, propVar);
                        if (SUCCEEDED(hr))
                        {
                            hr = spsCachedProperties->SetValue(PKEY_ItemType, propVar);
                        }
                        PropVariantClear(&propVar);
                    }
                }
            }
        }

        if (SUCCEEDED(hr))
        {
            if (SUCCEEDED(hr))
            {
                hr = CWMIFldr::s_CreateItemID(&wmiInfo, spsCachedProperties, ppidlChild);
            }
        }
    }

    return hr;
}


HRESULT CWMIFldr::s_GetStringProperty(IWbemClassObject *pObj, PCWSTR pszPropertyName, PROPVARIANT *ppropvar)
{
    CComVariant varProp;
    HRESULT hr = s_GetStringProperty(pObj, pszPropertyName, &varProp);
    if (SUCCEEDED(hr))
    {
        hr = VariantToPropVariant(&varProp, ppropvar);
    }
    return hr;
}

HRESULT CWMIFldr::s_GetStringProperty(IWbemClassObject *pObj, PCWSTR pszPropertyName, VARIANT *pvar)
{
    CIMTYPE cimtype;
    HRESULT hr = pObj->Get(pszPropertyName, 0, pvar, &cimtype, NULL);
    if (SUCCEEDED(hr))
    {
        if (cimtype != CIM_STRING || 
            // because an empty string is the same as null in BSTRs we need to allow
            // for an empty property.
            (pvar->vt != VT_BSTR && pvar->vt != VT_NULL))
        {
            VariantClear(pvar);
            hr = E_INVALIDARG;
        }
    }

    return hr;
}

HRESULT CWMIFldr::_CreateFolder(PCUITEMID_CHILD pidlChild, REFIID riid, __deref_out void **ppv)
{
    *ppv = NULL;
    CComPtr<IPersistFolder2> spChildFolder;
    HRESULT hr = CreateInstance(NULL, IID_PPV_ARGS(&spChildFolder));
    if (SUCCEEDED(hr))
    {
        PIDLIST_ABSOLUTE pidlChildFull = ILCombine(_pidl, pidlChild);
        if (pidlChildFull)
        {
            hr = spChildFolder->Initialize(pidlChildFull);
            if (SUCCEEDED(hr))
            {
                hr = spChildFolder->QueryInterface(riid, ppv);
            }
            ILFree(pidlChildFull);
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }
    return hr;
}

HRESULT CWMIFldr::_GetChildCount(PCUITEMID_CHILD pidlChild, VARIANT *pv)
{
    HRESULT hr;

    CComPtr<IShellFolder> spChildFolder;
    hr = _CreateFolder(pidlChild, IID_PPV_ARGS(&spChildFolder));
    if (SUCCEEDED(hr))
    {
        CComPtr<IEnumIDList> spEnum;
        hr = spChildFolder->EnumObjects(NULL, 0, &spEnum);
        if (SUCCEEDED(hr))
        {
            hr = S_OK;
        }

        ULONG cChildren = 0;
        while (S_OK == hr)
        {
            PITEMID_CHILD rgChildBatch[10] = {0};
            ULONG celtFetched;
            hr = spEnum->Next(ARRAYSIZE(rgChildBatch), rgChildBatch, &celtFetched);
            if (SUCCEEDED(hr))
            {
                cChildren += celtFetched;
                for (ULONG iFetched = 0; iFetched < celtFetched; iFetched++)
                {
                    ILFree(rgChildBatch[iFetched]);
                    rgChildBatch[iFetched] = NULL;
                }
            }
        }
        if (SUCCEEDED(hr))
        {
            hr = InitVariantFromUInt32(cChildren, pv);
        }
    }
    return hr;
}